﻿using System;
using System.Collections.Generic;
using System.Text;
using System.IO.Ports;


namespace KZ400B.Link
{
    public class SerialPortLink1:LinkBase
    {
        private SerialPort m_SPort;
        private string m_SPortName;
        private object m_readLokcer = new object();
        private byte[] m_recvData;
        private void clearRecvData()
        {
            clearRecvData(1024);
        }

        private void clearRecvData(int size)
        {
            lock (m_readLokcer)
            {
                if (size == 0)
                {
                    m_recvData = null;
                }
                //if (m_recvData == null)
                else
                {
                    m_recvData = new byte[size];
                }
                if (m_recvData != null)
                {
                    Array.Clear(m_recvData, 0, m_recvData.Length);
                }
            }
        }

        public SerialPortLink1()
        {
            m_SPort = null;
            m_SPortName = string.Empty;
            clearRecvData();
        }

        private Exception m_LastException;

        public Exception LastException { get { return m_LastException; } }

        public bool Open(SPort port)
        {
            m_SPortName = port.PortName;
            m_SPort = new SerialPort(m_SPortName);
            m_SPort.BaudRate = port.BaudRate;
            m_SPort.DataBits = port.DataBits;
            m_SPort.Parity = port.Parity;
            m_SPort.StopBits = port.StopBits;
            //m_SPort.Encoding = SPort.getPortEncoding( port.Encoding );

            try
            {
                //m_SPort.ReadTimeout = 500;
                m_SPort.Open();
                clearRecvData();
                return m_SPort.IsOpen;
            }
            catch (Exception ex)
            {
                m_LastException = ex;
                Utility.FileOperate.WriteToLog(
                    string.Format("SerialPort [{0}] open Error : {1}", m_SPortName, ex.Message)
                    );
            }
            return false;
        }

        public override bool Open(string ip, int port)
        {
            SPort sport = new SPort();
            sport.PortName = ip;
            return Open(sport);
        }

        public override bool Open(string name)
        {
            SPort sport = new SPort();
            sport.PortName = name;
            return Open(sport);
        }

        public void Close()
        {
            if (m_SPort != null && m_SPort.IsOpen)
            {
                m_SPort.Close();
                m_SPort = null;
                clearRecvData(0);
            }
        }

        public new bool IsOpen
        {
            get { return m_SPort != null && m_SPort.IsOpen; }
        }

        public new bool CanRead
        {
            get { return IsOpen; }
        }

        public new bool CanWrite
        {
            get { return IsOpen; }
        }



        public override int Write(string data)
        {
            if (string.IsNullOrEmpty(data))
            {
                Utility.FileOperate.WriteToLog("data is null");
                return ErrorCode.NoData;
            }
            byte[] bDatas = new byte[data.Length];
            Array.Clear(bDatas, 0, data.Length);
            int idx = 0;
            foreach (char c in data)
            {
                bDatas[idx] = (byte)c;
                idx++;
            }
            return Write(bDatas);
        }

        public int Write(byte[] data)
        {
            if (data == null || data.Length <= 0)
            {
                Utility.FileOperate.WriteToLog("data is null");
                return ErrorCode.NoData;
            }
            if (CanWrite)
            {
                try
                {
                    m_SPort.Write(data, 0, data.Length);
                    Utility.FileOperate.WriteToLog(
                        string.Format("SerialPort [{0}] Write  : {1}", m_SPortName ?? "Empty", HexCon.CharBytesToString(data))
                        );
                    return data.Length;
                }
                catch (Exception ex)
                {
                    m_LastException = ex;
                    Utility.FileOperate.WriteToLog(
                        string.Format("SerialPort [{0}] Write Error : {1}", m_SPortName ?? "Empty", ex.Message)
                        );
                    return ErrorCode.Exceptions;
                }
            }
            else
            {
                Utility.FileOperate.WriteToLog("SerialPort not open");
                return ErrorCode.NotOpen;
            }
        }

        public string getReadString()
        {
            StringBuilder rB = new StringBuilder();

            lock (m_readLokcer)
            {
                if (m_recvData != null && m_recvData.Length > 0)
                {
                    foreach (byte rData in m_recvData)
                    {
                        if (rData != 0)
                        {
                            rB.Append((char)rData);
                        }
                        else { break; }
                    }
                }
            }
            
            return rB.ToString();
        }

        public byte[] getReadBytes()
        {
            lock (m_readLokcer)
            {
                return m_recvData;
            }
        }

        public override string ReadString()
        {
            if (Read(1024) > 0)
            {
                return getReadString();
            }
            return string.Empty;
        }

        public byte[] ReadBytes()
        {
            if (Read(1024) > 0)
            {
                return getReadBytes();
            }
            return null;
        }

        public int Read(int size)
        {
            int nReads = 0;
            clearRecvData();
            if (CanRead)
            {
                try
                {
                    byte[] data = new byte[size];

                    int readRst = 0;
                    while(true)
                    {
                        try
                        {
                            readRst = m_SPort.ReadByte();
                            if (readRst == -1)
                            {
                                break;
                            }
                            else
                            {
                                byte byteRst = (byte)readRst;
                                data[nReads++] = byteRst;
                                if (byteRst == (byte)CommFlag.AllEnd[0])
                                {
                                    break;
                                }
                            }
                        }
                        catch (Exception ex)
                        {
                            Utility.FileOperate.WriteToLog(
                                string.Format("SerialPort [{0}] Read Error : {1}", m_SPortName ?? "Empty", ex.Message)
                                );
                            break;
                        }
                    }

                    //nReads = m_SPort.Read(data, 0, size);
                    if (nReads > 0)
                    {
                        lock (m_readLokcer)
                        {
                            clearRecvData(nReads);
                            Array.Copy(data, m_recvData, nReads);
                            Utility.FileOperate.WriteToLog(HexCon.CharBytesToString( data ) );
                        }
                    }
                    return nReads;
                }
                catch (Exception ex)
                {
                    //ArgumentNullException 传递的 buffer 为 null。 
                    //InvalidOperationException 指定的端口未打开。
                    //ArgumentOutOfRangeException offset 或 count 参数超出了所传递的 buffer 的有效区域。 offset 或 count 小于零。 
                    //ArgumentException offset 加上 count 大于 buffer 的长度。 
                    //TimeoutException 没有可以读取的字节。
                    Utility.FileOperate.WriteToLog(
                        string.Format("SerialPort [{0}] Read Error : {1}", m_SPortName ?? "Empty", ex.Message)
                        );
                    return ErrorCode.Exceptions;
                }
            }
            else
            {
                Utility.FileOperate.WriteToLog("SerialPort not open");
                return ErrorCode.NotOpen;
            }
            //return ErrorCode.UnkownError;
        }

        #region port settins
        public static string SetPortName(string defaultPortName)
        {
            string portName;

            Utility.FileOperate.WriteToLog("Available Ports:");
            foreach (string s in SerialPort.GetPortNames())
            {
                Utility.FileOperate.WriteToLog("   {0}", s);
            }

            Utility.FileOperate.WriteToLog("COM port({0}): ", defaultPortName);
            portName = Console.ReadLine();

            if (portName == "")
            {
                portName = defaultPortName;
            }
            return portName;
        }

        public static int SetPortBaudRate(int defaultPortBaudRate)
        {
            string baudRate;

            Utility.FileOperate.WriteToLog(
                string.Format("Baud Rate({0}): ", defaultPortBaudRate)
                );
            baudRate = Console.ReadLine();

            if (baudRate == "")
            {
                baudRate = defaultPortBaudRate.ToString();
            }

            return int.Parse(baudRate);
        }

        public static Parity SetPortParity(Parity defaultPortParity)
        {
            string parity;

            Utility.FileOperate.WriteToLog("Available Parity options:");
            foreach (string s in Enum.GetNames(typeof(Parity)))
            {
                Utility.FileOperate.WriteToLog(string.Format("   {0}", s));
            }

            Utility.FileOperate.WriteToLog(
                string.Format("Parity({0}):", defaultPortParity.ToString())
                );
            parity = Console.ReadLine();

            if (parity == "")
            {
                parity = defaultPortParity.ToString();
            }

            return (Parity)Enum.Parse(typeof(Parity), parity);
        }

        public static int SetPortDataBits(int defaultPortDataBits)
        {
            string dataBits;

            Utility.FileOperate.WriteToLog(
                string.Format("Data Bits({0}): ", defaultPortDataBits)
                );
            dataBits = Console.ReadLine();

            if (dataBits == "")
            {
                dataBits = defaultPortDataBits.ToString();
            }

            return int.Parse(dataBits);
        }

        public static StopBits SetPortStopBits(StopBits defaultPortStopBits)
        {
            string stopBits;

            Utility.FileOperate.WriteToLog("Available Stop Bits options:");
            foreach (string s in Enum.GetNames(typeof(StopBits)))
            {
                Utility.FileOperate.WriteToLog(string.Format("   {0}", s));
            }

            Utility.FileOperate.WriteToLog(
                string.Format("Stop Bits({0}):", defaultPortStopBits.ToString())
                );
            stopBits = Console.ReadLine();

            if (stopBits == "")
            {
                stopBits = defaultPortStopBits.ToString();
            }

            return (StopBits)Enum.Parse(typeof(StopBits), stopBits);
        }

        public static Handshake SetPortHandshake(Handshake defaultPortHandshake)
        {
            string handshake;

            Utility.FileOperate.WriteToLog("Available Handshake options:");
            foreach (string s in Enum.GetNames(typeof(Handshake)))
            {
                Utility.FileOperate.WriteToLog(string.Format("   {0}", s));
            }

            Utility.FileOperate.WriteToLog(
                string.Format("Stop Bits({0}):", defaultPortHandshake.ToString())
                );
            handshake = Console.ReadLine();

            if (handshake == "")
            {
                handshake = defaultPortHandshake.ToString();
            }

            return (Handshake)Enum.Parse(typeof(Handshake), handshake);
        }
        #endregion
    }
}
